using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

using Kenly.DBFramework.Gateway;

namespace Kenly.DBFramework.Reflection
{
    internal static class ColumnMapper
    {


        #region ReflectColumns


        /// <summary>
        /// Format : column1,column2,column3...
        /// </summary>
        /// <param name="entityType"></param>
        /// <returns>column1,column2,column3...</returns>
        public static string SelectAllColumnNameList(Type entityType)
        {
            List<string> columns = ReflectAllColumnNames(entityType);
            return BuildColumnNameList(columns);
        }

        /// <summary>
        /// Format : column1,column2,column3...
        /// </summary>
        /// <param name="entityType"></param>
        /// <param name="propertyNames"></param>
        /// <returns>column1,column2,column3...</returns>
        public static string SelectColumnNameList(Type entityType, List<string> propertyNames)
        {
            List<string> columns = PropertiesToSelectColumnNames(entityType, propertyNames);
            return BuildColumnNameList(columns);
        }

        /// <summary>
        /// Format : column1,column2,column3...
        /// </summary>
        /// <param name="entityType"></param>
        /// <param name="propertyNames"></param>
        /// <returns>column1,column2,column3...</returns>
        public static string InsertColumnNameList(Type entityType, List<string> propertyNames)
        {
            List<string> columns = PropertiesToInsertColumnNames(entityType, propertyNames);
            return BuildColumnNameList(columns);
        }

        /// <summary>
        /// Sql Server Format : @column1,@column2,@column3...
        /// Oracle Format : :column1,:column2,:column3...
        /// </summary>
        /// <param name="databaseType"></param>
        /// <param name="entityType"></param>
        /// <param name="propertyNames"></param>
        /// <returns></returns>
        public static string InsertParameterList(DatabaseType databaseType, Type entityType, List<string> propertyNames)
        {
            List<string> columns = PropertiesToInsertColumnNames(entityType, propertyNames);
            return BuildParameterList(columns, databaseType);
        }

        /// <summary>
        /// Sql Server Format : column1=@column1,column2=@column3,column3=@column3...
        /// Oracle Format : column1=:column1,column2=:column3,column3=:column3...
        /// </summary>
        /// <param name="databaseType"></param>
        /// <param name="entityType"></param>
        /// <param name="updatedByPK"></param>
        /// <param name="propertyNames"></param>
        /// <returns></returns>
        public static string UpdateColumnParameterPairs(DatabaseType databaseType, Type entityType, bool updatedByPK, List<string> propertyNames)
        {
            List<string> columns = PropertiesToUpdateColumnNames(entityType, updatedByPK, propertyNames);
            return BuildColumnAndParameterPairs(columns, databaseType);
        }


        #endregion

        #region Private Utility

        /// <summary>
        /// Reflect Column Name from entity's properite which contains a ColumnAttribute.
        /// </summary>
        /// <param name="entityType"></param>
        /// <returns></returns>
        private static List<string> ReflectAllColumnNames(Type entityType)
        {
            List<string> columnNames = new List<string>();
            if (entityType == null || entityType.GetProperties().Length <= 0)
            {
                return columnNames;
            }

            string columnName = "";
            ColumnAttribute columnAttribute = null;
            foreach (PropertyInfo propertyInfo in entityType.GetProperties())
            {
                columnAttribute = AttributeUtility.GetColumnAttribute(propertyInfo);
                if (columnAttribute != null)
                {
                    columnName = string.IsNullOrEmpty(columnAttribute.ColumnName) ? propertyInfo.Name : columnAttribute.ColumnName;
                    columnNames.Add(columnName);
                }
            }

            return columnNames;

        }

        private static List<string> PropertiesToSelectColumnNames(Type entityType, List<string> propertyNames)
        {
            List<string> columnNames = new List<string>();
            if (entityType == null || entityType.GetProperties().Length <= 0)
            {
                return columnNames;
            }

            string columnName = "";
            ColumnAttribute columnAttribute = null;
            foreach (string propertyName in propertyNames)
            {
                foreach (PropertyInfo propertyInfo in entityType.GetProperties())
                {
                    if (propertyInfo.Name != propertyName)
                    {
                        continue;
                    }
                    columnAttribute = AttributeUtility.GetColumnAttribute(propertyInfo);
                    if (columnAttribute != null)
                    {
                        columnName = string.IsNullOrEmpty(columnAttribute.ColumnName) ? propertyInfo.Name : columnAttribute.ColumnName;
                        columnNames.Add(columnName);
                    }
                }
            }

            return columnNames;
        }

        /// <summary>
        /// PropertiesToColumnNames for inserting.
        /// </summary>
        /// <param name="entityType"></param>
        /// <param name="propertyNames"></param>
        /// <returns></returns>
        private static List<string> PropertiesToInsertColumnNames(Type entityType, List<string> propertyNames)
        {
            List<string> columnNames = new List<string>();
            if (entityType == null || entityType.GetProperties().Length <= 0)
            {
                return columnNames;
            }

            string columnName = "";
            ColumnAttribute columnAttribute = null;
            foreach (string propertyName in propertyNames)
            {
                foreach (PropertyInfo propertyInfo in entityType.GetProperties())
                {
                    if (propertyInfo.Name != propertyName)
                    {
                        continue;
                    } 
                    columnAttribute = AttributeUtility.GetColumnAttribute(propertyInfo);
                    if (columnAttribute == null)
                    {
                        continue;
                    }
                    //Identity property will be excluded when inserting.
                    if (columnAttribute.IsIdentity)
                    {
                        continue;
                    }
                    columnName = string.IsNullOrEmpty(columnAttribute.ColumnName) ? propertyInfo.Name : columnAttribute.ColumnName;
                    columnNames.Add(columnName);
                }
            }

            return columnNames;
        }


        /// <summary>
        /// PropertiesToColumnNames for updating.
        /// </summary>
        /// <param name="entityType"></param>
        /// <param name="updatedByPK"></param>
        /// <param name="propertyNames"></param>
        /// <returns></returns>
        private static List<string> PropertiesToUpdateColumnNames(Type entityType, bool updatedByPK, List<string> propertyNames)
        {
            List<string> columnNames = new List<string>();
            if (entityType == null || entityType.GetProperties().Length <= 0)
            {
                return columnNames;
            }

            string columnName = "";
            ColumnAttribute columnAttribute = null;
            foreach (string propertyName in propertyNames)
            {
                foreach (PropertyInfo propertyInfo in entityType.GetProperties())
                {
                    if (propertyInfo.Name != propertyName)
                    {
                        continue;
                    }
                    columnAttribute = AttributeUtility.GetColumnAttribute(propertyInfo);
                    if (columnAttribute != null)
                    {
                        //Identity property will be excluded when updating.
                        if (columnAttribute.IsIdentity)
                        {
                            continue;
                        }
                        if (columnAttribute.IsPrimaryKey && updatedByPK)
                        {
                            continue;
                        }
                        columnName = string.IsNullOrEmpty(columnAttribute.ColumnName) ? propertyInfo.Name : columnAttribute.ColumnName;
                        columnNames.Add(columnName);
                    }
                }
            }
            return columnNames;
        }

        /// <summary>
        /// Format : column1,column2,column3...
        /// </summary>
        /// <param name="columnNames"></param>
        /// <returns></returns>
        private static string BuildColumnNameList(List<string> columnNames)
        {
            StringBuilder nameList = new StringBuilder();
            foreach (string columnName in columnNames)
            {
                nameList.Append(columnName);
                nameList.Append(",");
            }

            if (nameList.Length > 0)
            {
                nameList.Remove(nameList.Length - 1, 1);
            }
            return nameList.ToString();
        }


        /// <summary>
        ///Sql Server Format : @column1,@column2,@column3...
        /// Oracle Format : :column1,:column2,:column3...
        /// </summary>
        /// <param name="columnNames"></param>
        /// <param name="databaseType"></param>
        /// <returns></returns>
        private static string BuildParameterList(List<string> columnNames, DatabaseType databaseType)
        {
            StringBuilder nameList = new StringBuilder();
            foreach (string columnName in columnNames)
            {
                if (databaseType == DatabaseType.Oracle)
                {
                    nameList.Append(":" + columnName);
                }
                else
                {
                    nameList.Append("@" + columnName);
                }
                nameList.Append(",");
            }

            if (nameList.Length > 0)
            {
                nameList.Remove(nameList.Length - 1, 1);
            }
            return nameList.ToString();
        }


        /// <summary>
        ///  Sql Server Format : column1=@column1,column2=@column3,column3=@column3...
        ///  Oracle Format : column1=:column1,column2=:column3,column3=:column3...
        /// </summary>
        /// <param name="columnNames"></param>
        /// <param name="databaseType"></param>
        /// <returns></returns>
        private static string BuildColumnAndParameterPairs(List<string> columnNames, DatabaseType databaseType)
        {
            StringBuilder nameList = new StringBuilder();
            foreach (string columnName in columnNames)
            {
                nameList.Append(columnName);
                if (databaseType == DatabaseType.Oracle)
                {
                    nameList.Append("=:" + columnName);
                }
                else
                {
                    nameList.Append("=@" + columnName);
                }
                nameList.Append(",");
            }

            if (nameList.Length > 0)
            {
                nameList.Remove(nameList.Length - 1, 1);
            }
            return nameList.ToString();
        }

        #endregion

    }
}
